package binpack

import (
	"fmt"
	"gitee.com/ymofen/gobase"
	"io"
)

const (
	// key  max:255
	// data 0xFFFF max:65535
	// 包协议, minL: 8=1+2+2+3, bodystart:5
	// pre[1]+[0x80]keyl1+[0x80]keyl2 +dataL(LE:2)+data[key+data]+crc24[3]

	BinPackCrc32Str_MinL              = 9
	BinPackCrc32Str_DataStartIdx      = 5
	BinPackCrc32Str_PRE0         byte = 0xFB
)

func WriteBinPackCrc32Str(w io.Writer, key string, data []byte) (err error) {
	w.Write([]byte{BinPackCrc32Str_PRE0})

	l1 := len(key)
	l2 := len(data)
	if l1 > 0x3FFF {
		return fmt.Errorf("key must less than %d", 0x3FFF)
	}

	if l2 > 0xFFFF {
		return fmt.Errorf("data size must less than %d", 0xFFFF)
	}

	crc := uint32(0)

	l1_1 := (l1 & 0x3F80) >> 7 // 高7位
	l1_0 := l1 & 0x007F        // 低7位

	buf := []byte{byte(0x80 | l1_1), byte(0x80 | l1_0), byte(l2 >> 8), byte(l2)}
	w.Write(buf)
	crc = gobase.CRC32Continue(crc, buf...)

	buf = []byte(key)
	w.Write(buf)
	crc = gobase.CRC32Continue(crc, buf...)

	w.Write(data)
	crc = gobase.CRC32Continue(crc, data...)

	// crc32
	w.Write([]byte{byte(crc >> 24), byte(crc >> 16), byte(crc >> 8), byte(crc)})
	return nil
}

type BinPackCrc32StrDecode struct {
	decodestep int8

	keyl   int
	datal  int
	cachel int
	msgl   int
	cache  gobase.BytesBuilder
}

func (this *BinPackCrc32StrDecode) ExtractMsg() (key string, data []byte) {
	if this.keyl > 0 {
		key = string(this.cache.Buff(BinPackCrc32Str_DataStartIdx, this.keyl))
	}
	if this.datal > 0 {
		data = this.cache.Buff(BinPackCrc32Str_DataStartIdx+this.keyl, this.datal)
	}
	return
}

// 0: need more
// 1: ok
// -1: crc
// -2: errdata
func (this *BinPackCrc32StrDecode) InputByte(v byte) int {
	if this.decodestep < 0 {
		this.cache.Reset()
		this.keyl = 0
		this.cachel = 0
		this.datal = 0
		this.msgl = BinPackCrc32Str_MinL
		this.decodestep = 1
	}

	this.cache.WriteByte(v)
	this.cachel++
	if this.cachel > BinPackCrc32Str_DataStartIdx {
		if this.cachel < this.msgl {
			return 0
		} else {
			crc0 := this.cache.Uint32_BE(this.cachel - 4)
			crc1 := gobase.CRC32Buf(this.cache.BuffEx(1, this.cachel-4))
			this.decodestep = -1
			if crc1 != crc0 {
				return -1
			}
			return 1
		}
	}
	if this.cachel == 1 {
		if v != BinPackCrc32Str_PRE0 {
			this.decodestep = -1
			return -2
		}
	} else if this.cachel == 2 {
		if v&0x80 != 0x80 {
			this.decodestep = -1
			return -2
		}
		this.keyl = int(v) & 0x7F << 7
	} else if this.cachel == 3 {
		if v&0x80 != 0x80 {
			this.decodestep = -1
			return -2
		}
		this.keyl += int(v & 0x7F)
	} else if this.cachel == 5 {
		this.datal = int(this.cache.Uint16_BE(3))
		this.msgl = BinPackCrc32Str_MinL + this.keyl + this.datal
	}

	return 0
}
